import { create } from 'zustand'
// import { Parser } from 'expr-eval'
// const parser = new Parser();
//  const res = parser.evaluate(expression);
// 用于实现测试用例的调用堆栈
class CallStack {
    constructor() {
        this.stack = [];
    }
    push(context) {
        this.stack.push(context);
    }
    pop() {
        return this.stack.pop();
    }
    current() {
        return this.stack[this.stack.length - 1];
    }
}

// 用于注册消息
class EventEmitter {
    constructor() {
        this.events = {};
    }

    on(event, listener) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener);
    }

    emit(event, ...args) {
        if (this.events[event]) {
            this.events[event].forEach(listener => listener(...args));
        }
    }
}

// 用于管理变量
class Variables {

    storedVars = new Map() // 存储的变量

    replaceVariables(str) {
        return typeof str === 'string' ? str.replace(/\${(.*?)}/g, (_, varName) => {
            return this.get(varName) ?? '';
        }) : str;
    }

    getPropertyValue(obj1, dataToRetrieve) {
        return dataToRetrieve.split('.').reduce(function (o, k) {
            const arr = /([^.\n]+)\[(\d*)\]/.exec(k)
            if (arr) {
                return o && o[arr[1]][arr[2]]
            }
            return o && o[k]
        }, obj1)
    }

    // 获取变量
    get(key) {
        if (this.storedVars.get(key) == undefined) {
            if (/(\.)/.exec(key)) {
                //a.b , a[0].b, a.b[3].c
                const propertyAccess = /([^.\n]+)\.(.*)/.exec(key) || []
                if (this.storedVars.has(propertyAccess[1])) {
                    //a.b, a.b[0].c
                    const r3 = this.getPropertyValue(
                        this.storedVars.get(propertyAccess[1]),
                        propertyAccess[2]
                    )
                    return r3
                } else if (/([^.\n]+)\[(\d*)\]/.exec(propertyAccess[1])) {
                    //a[0].b
                    const arrayAccess = /([^.\n]+)\[(\d*)\]/.exec(propertyAccess[1]) || []
                    if (this.storedVars.has(arrayAccess[1])) {
                        const r3 = this.getPropertyValue(
                            this.storedVars.get(arrayAccess[1])[arrayAccess[2]],
                            propertyAccess[2]
                        )
                        return r3
                    }
                }
            } else if (/([^.\n]+)\[(\d*)\]/.exec(key)) {
                //a[0]
                const arrayAccess = /([^.\n]+)\[(\d*)\]/.exec(key) || []
                if (this.storedVars.has(arrayAccess[1])) {
                    const r3 = this.storedVars.get(arrayAccess[1])
                    return r3[arrayAccess[2]]
                }
            }
            return this.storedVars.get(key)
        }
        return this.storedVars.get(key)
    }

    set(key, value) {
        this.storedVars.set(key, value)
    }

    has(key) {
        if (this.storedVars.has(key) == false) {
            if (/(\.)/.exec(key)) {
                //a.b or a[0].b or a.b[0].c
                const propertyAccess = /([^.\n]+)\.(.*)/.exec(key) || []
                if (
                    this.storedVars.has(propertyAccess[1]) == false &&
                    /([^.\n]+)\[(\d*)\]/.exec(propertyAccess[1])
                ) {
                    //arr[0].a
                    const arrayAccess = /([^.\n]+)\[(\d*)\]/.exec(propertyAccess[1]) || []
                    return this.storedVars.has(arrayAccess[1])
                } else return this.storedVars.has(propertyAccess[1])
            } else if (/([^.\n]+)\[(\d*)\]/.exec(key)) {
                // a[0]
                const arrayAccess = /([^.\n]+)\[(\d*)\]/.exec(key) || []
                return this.storedVars.has(arrayAccess[1])
            }
        }
        return this.storedVars.has(key)
    }

    delete(key) {
        if (this.storedVars.has(key)) this.storedVars.delete(key)
        else if (/(\.)/.exec(key)) {
            const propertyAccess = /([^.]+)\.(.*)/.exec(key) || []
            this.storedVars.delete(propertyAccess[1])
        }
    }

    clear() {
        this.storedVars.clear()
    }
}

// 配置信息
const config = {
    preDelay: 100, // step执行前延迟
    postDelay: 100, // step执行后延迟
}
// 子测试用例
const subTestCase = {
    id: '子用例',
    steps: [
        {
            cmd: 'echo', // 打印命令
            target: '子用例开始执行',
            value: '',
            description: '说明：打印日志',
            steps: [],
            selected: true, // 执行这步时要将selected设为true
        },
        {
            breakpoint: false, // 遇到断点，暂停执行
            cmd: 'executeScript', // 执行脚本命令
            target: '1 + 1', // 脚本表达式
            value: 'myValue', // 变量名
            description: '说明：执行脚本并把结果保存在变量myValue中',
            steps: [],
        },
        {
            cmd: 'echo',
            target: '子用例执行结束，myValue值为${myValue}',
            value: '',
            description: '说明：打印日志',
            steps: [],
        },
    ]
}
// 测试用例1
const testCase1 = {
    id: '流程功能测试1',
    type: 'file',
    steps: [
        {
            cmd: 'echo',
            target: '流程功能测试1开始执行',
            value: '',
            description: '说明：打印日志',
            steps: [],
        },
        {
            breakpoint: true, // 遇到断点，暂停执行
            cmd: 'setVar', // 设置变量命令
            target: 'a',
            value: 8,
            description: '说明：将变量a的值设为8',
            steps: [],
        },
        {
            cmd: 'if', // if命令
            target: '${a} > 10', // if语句的条件
            value: '',
            description: '说明：如果变量a的值大于10',
            steps: [
                { cmd: 'echo', target: 'if条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a}>10', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'else if', // else if 命令: 要求前一个命令必须为if或else if
            target: '${a} > 5', // else if的条件
            value: '',
            description: '说明：如果变量a的值大于5',
            steps: [
                { cmd: 'echo', target: 'else if条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a}>5', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'else if', // else if 命令: 要求前一个命令必须为if或else if
            target: '${a} > 3', // else if的条件
            value: '',
            description: '说明：如果变量a的值大于3',
            steps: [
                { cmd: 'echo', target: 'else if条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a} > 3', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'else',  // else 命令: 要求前一个命令必须为if或else if
            target: '',
            value: '',
            description: '说明：否则',
            steps: [
                { cmd: 'echo', target: 'else 条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a}<=5', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'if', // if命令
            target: '${a} > 10', // if语句的条件
            value: '',
            description: '说明：如果变量a的值大于10',
            steps: [
                { cmd: 'echo', target: 'if条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a}>10', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'else', // else 命令: 要求前一个命令必须为if或else if
            target: '',
            value: '',
            description: '说明：否则',
            steps: [
                { cmd: 'echo', target: 'else 条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a}<=5', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'run', // run命令：可以执行其它用例
            target: '其它用例/子用例', // 执行子用例：在当前用例所在目录中查找子用例
            value: '',
            description: '说明：调用运行子用例',
            steps: [],
        },
        {
            cmd: 'echo',
            target: '验证子用例执行结果：${myValue}', // 打印内容
            value: '',
            description: '说明：打印日志',
            steps: [],
        },
        {
            cmd: 'while', // while命令：用于执行循环
            target: '${myValue} > 0', // while循环的条件
            value: '',
            description: '说明：执行while循环',
            steps: [
                {
                    cmd: 'executeScript',
                    target: '${myValue} - 1',
                    value: 'myValue',
                    description: '说明：myValue值自减',
                    steps: [],
                },
            ]
        },
        {
            cmd: 'echo',
            target: '流程功能测试1执行结束',
            value: '',
            description: '说明：打印日志',
            steps: [],
        },
    ]
}
// 测试用例2
const testCase2 = {
    id: '流程功能测试2',
    type: 'file',
    steps: [
        {
            cmd: 'echo',
            target: '流程功能测试2开始执行',
            value: '',
            description: '说明：打印日志',
            steps: [],
        },
        {
            cmd: 'setVar',
            target: 'a',
            value: 8,
            description: '说明：将变量a的值设为8',
            steps: [],
        },
        {
            cmd: 'if',
            target: '${a} > 10',
            value: '',
            description: '说明：如果变量a的值大于10',
            steps: [
                {
                    cmd: 'if',
                    target: '${a} > 11',
                    value: '',
                    description: '说明：如果变量a的值大于11',
                    steps: [
                        { cmd: 'echo', target: 'if条件成立', value: '', description: '说明：打印日志', steps: [] },
                        { cmd: 'echo', target: '判断条件:${a} >  11', value: '', description: '说明：打印日志', steps: [] },
                    ],
                },
                {
                    cmd: 'else if',
                    target: '${a} > 12',
                    value: '',
                    description: '说明：如果变量a的值大于12',
                    steps: [
                        { cmd: 'echo', target: 'else if条件成立', value: '', description: '说明：打印日志', steps: [] },
                        { cmd: 'echo', target: '判断条件:${a} > 12', value: '', description: '说明：打印日志', steps: [] },
                    ],
                },
                {
                    cmd: 'else',
                    target: '',
                    value: '',
                    description: '说明：否则',
                    steps: [
                        { cmd: 'echo', target: 'else条件成立', value: '', description: '说明：打印日志', steps: [] },
                        { cmd: 'echo', target: '判断条件: else', value: '', description: '说明：打印日志', steps: [] },
                    ],
                },
                { cmd: 'echo', target: 'if条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件: ${a} > 10', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'else if',
            target: '${a} > 5',
            value: '',
            description: '说明：如果变量a的值大于5',
            steps: [
                { cmd: 'echo', target: 'else if条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a} > 5', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'else',
            target: '',
            value: '',
            description: '说明：否则',
            steps: [
                { cmd: 'echo', target: 'else 条件成立', value: '', description: '说明：打印日志', steps: [] },
                { cmd: 'echo', target: '判断条件:${a}<=5', value: '', description: '说明：打印日志', steps: [] },
            ],
        },
        {
            cmd: 'echo',
            target: '流程功能测试2执行结束',
            value: '',
            description: '说明：打印日志',
            steps: [],
        },
    ]
}
// 用例目录
const folder = [testCase1, testCase2, {
    id: "其它用例",
    type: 'folder',
    children: [
        subTestCase
    ]
}];
// 测试用例集: 某一目录下的所有测试用例为测试用例集
const testSuits = folder.filter(f => f.type === 'file');

const initialState = {
    // 配置信息
    config: {
        preDelay: 100, // step执行前延迟
        postDelay: 100, // step执行后延迟 
    },
    folder,
    variables: [],
    logs: [],
    editor: {
        testCase: testCase1
    },
    runner: {
        status: 'idle', // 'running' | 'paused' | 'stopped', // 当前运行的状态
        currentTestCase: '流程功能测试1', // 当前运行的用例id
        steps: [], // 当前运行的用例的steps
    }
}

const useStore = create((set, get) => ({
    ...initialState,
}))

const state = useStore.getState()

/*
使用zustand实现一套完整的测试用例的执行流程：
1、可以执行某个用例集；
2、可以单独执行某个测试用例;
3、可以选择执行循环次数，执行用例集或测试用例;
4、请按照if、else if、else、while的表达式结果,控制执行流程；
5、执行过程中可以手动暂停、继续、终止；
6、可以选择某测试用例的step从此步执行；
7、可以单步执行某个测试用例；
8、打印step的执行结果；不打印description的内容
9、 step的执行后完成后，通过递归执行下一个step
10、step执行失败后自动重试5000ms;
11、重试失败后终止。
12、通过CallStack管理测试用例的执行顺序
13、遇到breakpoint:true的step，要自动暂停
14、暂停功能使用一个挂起的Promise实现
15、注册EventEmitter事件，当暂停、继续、终止、成功、失败时通过emit发送通知消息
16、变量通过Variables管理
17、使用zustand管理状态运行状态等更新到store中
18、表达式计算结果，使用安全的做法，不要使用eval
19、执行到某step时，要设置selected:true用于标识正在执行这行,tep执行成功，要设置success:true
*/
